package com.fyy.shortvideo;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.Area;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.media.MediaRecorder;
import android.media.MediaRecorder.AudioEncoder;
import android.media.MediaRecorder.AudioSource;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OutputFormat;
import android.media.MediaRecorder.VideoEncoder;
import android.media.MediaRecorder.VideoSource;
import android.os.Build;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.widget.LinearLayout;

import com.utils.DeviceUtil;
import com.utils.SurfaceViewHelper;

/**
 * 视频播放控件 Created by Wood on 2016/4/6.
 */
public class MovieRecorderView extends LinearLayout implements OnErrorListener {
	private static final String LOG_TAG = "MovieRecorderView";

	private Context context;

	private SurfaceView surfaceView;
	private SurfaceHolder surfaceHolder;
	private ProgressView progressBar;

	private MediaRecorder mediaRecorder;
	private Camera camera;
	private Timer timer;// 计时器

	private int mWidth;// 视频录制分辨率宽度
	private int mHeight;// 视频录制分辨率高度
	private boolean isFirstOpen;// 是否一开始就打开摄像头
	private boolean isOpenCamera;
	private int recordMaxTime;// 最长拍摄时间
	private int timeCount;// 时间计数
	private File recordFile = null;// 视频文件
	private long sizePicture = 0;

	private SurfaceViewHelper helper;
	private GestureDetector mDetector;
	private View.OnTouchListener mOnSurfaveViewTouchListener = new View.OnTouchListener() {
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			if (camera == null || !isOpenCamera) {
				return false;
			}
			return mDetector.onTouchEvent(event);
		}

	};

	public MovieRecorderView(Context context) {
		this(context, null);
	}

	public MovieRecorderView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	@TargetApi(Build.VERSION_CODES.HONEYCOMB)
	public MovieRecorderView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.context = context;
		helper = SurfaceViewHelper.newInstance(context);

		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.MovieRecorderView, defStyle, 0);
		mWidth = a.getInteger(R.styleable.MovieRecorderView_record_width, 640);// 默认640
		mHeight = a
				.getInteger(R.styleable.MovieRecorderView_record_height, 360);// 默认360

		isFirstOpen = a.getBoolean(
				R.styleable.MovieRecorderView_is_open_camera, true);// 默认打开摄像头
		recordMaxTime = a.getInteger(
				R.styleable.MovieRecorderView_record_max_time, 10);// 默认最大拍摄时间为10s

		LayoutInflater.from(context)
				.inflate(R.layout.movie_recorder_view, this);
		surfaceView = (SurfaceView) findViewById(R.id.surfaceview);
		mDetector = new GestureDetector(context, new ZoomGestureListener());
		surfaceView.setOnTouchListener(mOnSurfaveViewTouchListener);
		// TODO 需要用到进度条，打开此处，也可以自己定义自己需要的进度条，提供了拍摄进度的接口
		progressBar = (ProgressView) findViewById(R.id.progressBar);
		progressBar.setMax(recordMaxTime);// 设置进度条最大量
		findViewById(R.id.change).setOnClickListener(
				new View.OnClickListener() {

					@Override
					public void onClick(View v) {
						camera = helper.changePreview(camera, surfaceHolder);
						isOpenCamera = true;
						try {
							initCamera();
						} catch (IOException e) {
							e.printStackTrace();
						}
						isOpenCamera = false;
						System.out.println("#camera:" + camera);
					}
				});
		surfaceHolder = surfaceView.getHolder();
		surfaceHolder.addCallback(new CustomCallBack());
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

		a.recycle();
	}

	/**
	 * SurfaceHolder回调
	 */
	private class CustomCallBack implements Callback {
		@Override
		public void surfaceCreated(SurfaceHolder holder) {
			System.out.println("#surfaceCreated");
			if (!isFirstOpen)
				return;
			try {
				initCamera();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width,
				int height) {
			System.out.println("#surfaceChanged");

		}

		@Override
		public void surfaceDestroyed(SurfaceHolder holder) {
			System.out.println("#surfaceDestroyed");
			if (!isFirstOpen)
				return;
			freeCameraResource();
		}
	}

	/**
	 * 初始化摄像头
	 */
	public void initCamera() throws IOException {
		System.out.println("#initCamera");
		if (!isOpenCamera) {
			if (camera != null) {
				freeCameraResource();
			}
			try {
				if (checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_BACK)) {
					camera = helper.open(Camera.CameraInfo.CAMERA_FACING_BACK);
				} else if (checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT)) {
					camera = helper.open(Camera.CameraInfo.CAMERA_FACING_FRONT);// TODO
				}
			} catch (Exception e) {
				e.printStackTrace();
				freeCameraResource();
				((Activity) context).finish();
			}
		}
		if (camera == null)
			return;

		setCameraParams();
		camera.setDisplayOrientation(90);
		camera.setPreviewDisplay(surfaceHolder);
		camera.startPreview();
		camera.unlock();

		isOpenCamera = true;
	}

	/**
	 * 检查是否有摄像头
	 * 
	 * @param facing
	 *            前置还是后置
	 * @return
	 */
	private boolean checkCameraFacing(int facing) {
		int cameraCount = Camera.getNumberOfCameras();
		Camera.CameraInfo info = new Camera.CameraInfo();
		for (int i = 0; i < cameraCount; i++) {
			Camera.getCameraInfo(i, info);
			if (facing == info.facing) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 设置摄像头为竖屏
	 */
	private void setCameraParams() {
		if (camera != null) {
			Parameters params = camera.getParameters();
			params.set("orientation", "portrait");
			List<Camera.Size> supportedPictureSizes = params
					.getSupportedPictureSizes();
			for (Camera.Size size : supportedPictureSizes) {
				sizePicture = (size.height * size.width) > sizePicture ? size.height
						* size.width
						: sizePicture;
			}
			params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
			// LogUtil.e(LOG_TAG,"手机支持的最大像素supportedPictureSizes===="+sizePicture);
			setPreviewSize(params);
			params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
			camera.setParameters(params);
		}
	}

	/**
	 * 根据手机支持的视频分辨率，设置预览尺寸
	 * 
	 * @param params
	 */
	private void setPreviewSize(Parameters params) {
		if (camera == null) {
			return;
		}
		// 获取手机支持的分辨率集合，并以宽度为基准降序排序
		List<Camera.Size> previewSizes = params.getSupportedPreviewSizes();
		Collections.sort(previewSizes, new Comparator<Camera.Size>() {
			@Override
			public int compare(Camera.Size lhs, Camera.Size rhs) {
				if (lhs.width > rhs.width) {
					return -1;
				} else if (lhs.width == rhs.width) {
					return 0;
				} else {
					return 1;
				}
			}
		});

		float tmp = 0f;
		float minDiff = 100f;
		float ratio = 3.0f / 4.0f;// TODO 高宽比率3:4，且最接近屏幕宽度的分辨率，可以自己选择合适的想要的分辨率
		Camera.Size best = null;
		for (Camera.Size s : previewSizes) {
			tmp = Math.abs(((float) s.height / (float) s.width) - ratio);
			Log.e(LOG_TAG, "setPreviewSize: width:" + s.width + "...height:"
					+ s.height);
			// LogUtil.e(LOG_TAG,"tmp:" + tmp);
			if (tmp < minDiff) {
				minDiff = tmp;
				best = s;
			}
		}
		// LogUtil.e(LOG_TAG, "BestSize: width:" + best.width + "...height:" +
		// best.height);
		// List<int[]> range = params.getSupportedPreviewFpsRange();
		// int[] fps = range.get(0);
		// LogUtil.e(LOG_TAG,"min="+fps[0]+",max="+fps[1]);
		// params.setPreviewFpsRange(3,7);

		Point size = new Point();
		size.x = surfaceView.getMeasuredWidth();
		size.y = surfaceView.getMeasuredHeight();
		System.out.println("#size:" + size);
		best = getBestCameraRatio(params, size);// 1920 1080 1280 960
		params.setPreviewSize(best.width, best.height);// 预览比率

		// params.setPictureSize(480, 720);//拍照保存比率

		Log.e(LOG_TAG, "#setPreviewSize BestSize: width:" + best.width
				+ "...height:" + best.height);

		// TODO 大部分手机支持的预览尺寸和录制尺寸是一样的，也有特例，有些手机获取不到，那就把设置录制尺寸放到设置预览的方法里面
		if (params.getSupportedVideoSizes() == null
				|| params.getSupportedVideoSizes().size() == 0) {
			mWidth = best.width;
			mHeight = best.height;
		} else {
			setVideoSize(params);
		}
	}

	private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
		final double ASPECT_TOLERANCE = 0.1;
		double targetRatio = (double) w / h;
		if (sizes == null)
			return null;

		Size optimalSize = null;
		double minDiff = Double.MAX_VALUE;

		int targetHeight = h;

		// Try to find an size match aspect ratio and size
		for (Size size : sizes) {
			// 此处注意:camera的宽为屏幕竖屏的高,camera的高为屏幕竖屏的宽
			double ratio = (double) size.height / size.width;
			if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
				continue;
			if (Math.abs(size.height - targetHeight) < minDiff) {
				optimalSize = size;
				minDiff = Math.abs(size.height - targetHeight);
			}
		}

		// Cannot find the one match the aspect ratio, ignore the requirement
		if (optimalSize == null) {
			minDiff = Double.MAX_VALUE;
			for (Size size : sizes) {
				if (Math.abs(size.height - targetHeight) < minDiff) {
					optimalSize = size;
					minDiff = Math.abs(size.height - targetHeight);
				}
			}
		}
		return optimalSize;
	}

	private Size getBestCameraRatio(Camera.Parameters parameters,
			Point screenResolution) {
		Size best = null;
		List<Size> supportedPreviewSizes = parameters
				.getSupportedPreviewSizes();
		best = getOptimalPreviewSize(supportedPreviewSizes, screenResolution.x,
				screenResolution.y);
		Log.e(LOG_TAG, "#getBestCameraResolution BestSize: width:" + best.width
				+ "...height:" + best.height);
		return best;
	}

	/**
	 * 根据手机支持的视频分辨率，设置录制尺寸
	 * 
	 * @param params
	 */
	private void setVideoSize(Parameters params) {
		if (camera == null) {
			return;
		}
		// 获取手机支持的分辨率集合，并以宽度为基准降序排序
		List<Camera.Size> previewSizes = params.getSupportedVideoSizes();
		Collections.sort(previewSizes, new Comparator<Camera.Size>() {
			@Override
			public int compare(Camera.Size lhs, Camera.Size rhs) {
				if (lhs.width > rhs.width) {
					return -1;
				} else if (lhs.width == rhs.width) {
					return 0;
				} else {
					return 1;
				}
			}
		});

		float tmp = 0f;
		float minDiff = 100f;
		float ratio = 3.0f / 4.0f;// 高宽比率3:4，且最接近屏幕宽度的分辨率
		Camera.Size best = null;
		for (Camera.Size s : previewSizes) {
			tmp = Math.abs(((float) s.height / (float) s.width) - ratio);
			Log.e(LOG_TAG, "setVideoSize: width:" + s.width + "...height:"
					+ s.height);
			if (tmp < minDiff) {
				minDiff = tmp;
				best = s;
			}
		}
		Log.e(LOG_TAG, "setVideoSize BestSize: width:" + best.width
				+ "...height:" + best.height);
		// 设置录制尺寸
		mWidth = best.width;
		mHeight = best.height;
	}

	/**
	 * 释放摄像头资源
	 */
	private void freeCameraResource() {
		try {
			if (camera != null) {
				camera.setPreviewCallback(null);
				camera.stopPreview();
				camera.lock();
				camera.release();
				camera = null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			camera = null;
		}
	}

	/**
	 * 创建视频文件
	 */
	private void createRecordDir() {
		File sampleDir = new File(Environment.getExternalStorageDirectory()
				+ File.separator + "SampleVideo/video/");
		if (!sampleDir.exists()) {
			sampleDir.mkdirs();
		}
		try {
			// TODO 文件名用的时间戳，可根据需要自己设置，格式也可以选择3gp，在初始化设置里也需要修改
			recordFile = new File(sampleDir, System.currentTimeMillis()
					+ ".mp4");
			// recordFile = new File(sampleDir, System.currentTimeMillis() +
			// ".mp4");
			// File.createTempFile(AccountInfo.userId, ".mp4", sampleDir);
			// LogUtil.e(LOG_TAG, recordFile.getAbsolutePath());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 录制视频初始化
	 */
	private void initRecord() throws Exception {
		mediaRecorder = new MediaRecorder();
		mediaRecorder.reset();
		if (camera != null)
			mediaRecorder.setCamera(camera);
		mediaRecorder.setOnErrorListener(this);
		mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
		mediaRecorder.setVideoSource(VideoSource.CAMERA);// 视频源
		mediaRecorder.setAudioSource(AudioSource.MIC);// 音频源
		mediaRecorder.setOutputFormat(OutputFormat.MPEG_4);// TODO 视频输出格式
															// 也可设为3gp等其他格式
		mediaRecorder.setAudioEncoder(AudioEncoder.AMR_NB);// 音频格式
		System.out.println("#mWidth:" + mWidth + " mHeight:" + mHeight);
		mediaRecorder.setVideoSize(mWidth, mHeight);// 设置分辨率
		// mediaRecorder.setVideoFrameRate(25);//TODO 设置每秒帧数
		// 这个设置有可能会出问题，有的手机不支持这种帧率就会录制失败，这里使用默认的帧率，当然视频的大小肯定会受影响
		// LogUtil.e(LOG_TAG,"手机支持的最大像素supportedPictureSizes===="+sizePicture);
		if (sizePicture < 3000000) {// 这里设置可以调整清晰度
			mediaRecorder.setVideoEncodingBitRate(3 * 1024 * 512);
		} else if (sizePicture <= 5000000) {
			mediaRecorder.setVideoEncodingBitRate(2 * 1024 * 512);
		} else {
			mediaRecorder.setVideoEncodingBitRate(1 * 1024 * 512);
		}
		if (helper.cameraPosition == Camera.CameraInfo.CAMERA_FACING_BACK) {
			mediaRecorder.setOrientationHint(90);// 输出旋转90度，保持竖屏录制
		} else if (helper.cameraPosition == Camera.CameraInfo.CAMERA_FACING_FRONT) {
			mediaRecorder.setOrientationHint(270);// 输出旋转90度，保持竖屏录制
		}

		mediaRecorder.setVideoEncoder(VideoEncoder.H264);// 视频录制格式
		// mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);
		mediaRecorder.setOutputFile(recordFile.getAbsolutePath());
		mediaRecorder.prepare();
		mediaRecorder.start();
	}

	/**
	 * 开始录制视频
	 * 
	 * @param onRecordFinishListener
	 *            达到指定时间之后回调接口
	 */
	public void record(final OnRecordFinishListener onRecordFinishListener) {
		this.onRecordFinishListener = onRecordFinishListener;
		createRecordDir();
		try {
			if (!isFirstOpen)
				initCamera();
			initRecord();
			timeCount = 0;// 时间计数器重新赋值
			timer = new Timer();
			timer.schedule(new TimerTask() {
				@Override
				public void run() {
					timeCount++;
					progressBar.setProgress(timeCount);// 设置进度条
					if (onRecordProgressListener != null) {
						onRecordProgressListener.onProgressChanged(
								recordMaxTime, timeCount);
					}

					// 达到指定时间，停止拍摄
					if (timeCount == recordMaxTime) {
						stop();
						if (MovieRecorderView.this.onRecordFinishListener != null)
							MovieRecorderView.this.onRecordFinishListener
									.onRecordFinish();
					}
				}
			}, 0, 1000);
		} catch (Exception e) {
			e.printStackTrace();
			if (mediaRecorder != null) {
				mediaRecorder.release();
			}
			freeCameraResource();
		}
	}

	/**
	 * 停止拍摄
	 */
	public void stop() {
		stopRecord();
		releaseRecord();
		freeCameraResource();
	}

	/**
	 * 停止录制
	 */
	public void stopRecord() {
		progressBar.setProgress(0);
		if (timer != null)
			timer.cancel();
		if (mediaRecorder != null) {
			mediaRecorder.setOnErrorListener(null);// 设置后防止崩溃
			mediaRecorder.setPreviewDisplay(null);
			try {
				mediaRecorder.stop();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 释放资源
	 */
	private void releaseRecord() {
		if (mediaRecorder != null) {
			mediaRecorder.setOnErrorListener(null);
			try {
				mediaRecorder.release();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		mediaRecorder = null;
	}

	/**
	 * 获取当前录像时间
	 * 
	 * @return timeCount
	 */
	public int getTimeCount() {
		return timeCount;
	}

	/**
	 * 设置最大录像时间
	 * 
	 * @param recordMaxTime
	 */
	public void setRecordMaxTime(int recordMaxTime) {
		this.recordMaxTime = recordMaxTime;
	}

	/**
	 * 返回录像文件
	 * 
	 * @return recordFile
	 */
	public File getRecordFile() {
		return recordFile;
	}

	/**
	 * 录制完成监听
	 */
	private OnRecordFinishListener onRecordFinishListener;

	/**
	 * 录制完成接口
	 */
	public interface OnRecordFinishListener {
		void onRecordFinish();
	}

	/**
	 * 录制进度监听
	 */
	private OnRecordProgressListener onRecordProgressListener;

	/**
	 * 设置录制进度监听
	 * 
	 * @param onRecordProgressListener
	 */
	public void setOnRecordProgressListener(
			OnRecordProgressListener onRecordProgressListener) {
		this.onRecordProgressListener = onRecordProgressListener;
	}

	/**
	 * 录制进度接口
	 */
	public interface OnRecordProgressListener {
		/**
		 * 进度变化
		 * 
		 * @param maxTime
		 *            最大时间，单位秒
		 * @param currentTime
		 *            当前进度
		 */
		void onProgressChanged(int maxTime, int currentTime);
	}

	public void setZoom(int zoomValue) {
		try {
			camera.reconnect();
			Parameters parameters = camera.getParameters();
			if (parameters.isZoomSupported()) {
				final int MAX = parameters.getMaxZoom();
				if (MAX == 0)
					return;
				if (zoomValue > MAX)
					zoomValue = MAX;
				parameters.setZoom(zoomValue); // value zoom value. The valid
												// range
				camera.setParameters(parameters);
				camera.unlock();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	class ZoomGestureListener extends SimpleOnGestureListener {
		private boolean mZoomIn = true;

		@Override
		public boolean onDoubleTap(MotionEvent e) {
			if (camera == null || !isOpenCamera) {
				return false;
			}

			if (!mZoomIn) {
				setZoom(10); // zoom in..
				mZoomIn = true;
			} else {
				setZoom(0); // zoom out..
				mZoomIn = false;
			}
			return true;
		}

		@Override
		public boolean onDown(MotionEvent e) {
			checkCameraFocus(e);
			return true;
		}
	}

	private void checkCameraFocus(MotionEvent event) {
		float x = event.getX();
		float y = event.getY();
		float touchMajor = event.getTouchMajor();
		float touchMinor = event.getTouchMinor();
		Rect touchRect = new Rect();
		// 触摸范围
		if (touchMajor == 0 && touchMinor == 0) {
			touchMajor = 100;
			touchMinor = 100;
		}
		touchRect.set((int) (x - touchMajor / 2), (int) (y - touchMinor / 2),
				(int) (x + touchMajor / 2), (int) (y + touchMinor / 2));

		System.out.println("#touchMajor:" + touchMajor + " touchMinor:"
				+ touchMinor + " x:" + x + " y:" + y);

		// 坐标转换为focusArea范围
		Rect focusRect = new Rect();
		focusRect.set(touchRect.left * 2000 / surfaceView.getWidth() - 1000,
				touchRect.top * 2000 / surfaceView.getHeight() - 1000,
				touchRect.right * 2000 / surfaceView.getWidth() - 1000,
				touchRect.bottom * 2000 / surfaceView.getHeight() - 1000);
		System.out.println("#focusRect:" + focusRect + " touchRect:"
				+ touchRect);
		System.out.println("#width:" + surfaceView.getWidth() + " height:"
				+ surfaceView.getHeight());
		if (focusRect.left >= focusRect.right
				|| focusRect.top >= focusRect.bottom)
			return;

		ArrayList<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
		focusAreas.add(new Camera.Area(focusRect, 1000));
		if (!manualFocus(new Camera.AutoFocusCallback() {

			@Override
			public void onAutoFocus(boolean success, Camera camera) {
				// if (success) {
				System.out.println("#onAutoFocus previewsize..width = "
						+ camera.getParameters().getPreviewSize().width
						+ "\nheight = "
						+ camera.getParameters().getPreviewSize().height);
				// }
			}
		}, focusAreas)) {
		}

	}

	public boolean manualFocus(AutoFocusCallback cb, List<Area> focusAreas) {
		// 判断系统是否是4.0以上的版本
		if (camera != null && DeviceUtil.isICS()) {
			try {
				camera.reconnect();
				camera.cancelAutoFocus();
				Parameters parameters = camera.getParameters();
				if (parameters != null) {
					if (focusAreas != null) {
						// getMaxNumFocusAreas检测设备是否支持
						if (parameters.getMaxNumFocusAreas() > 0) {
							parameters.setFocusAreas(focusAreas);
						}
						// getMaxNumMeteringAreas检测设备是否支持
						if (parameters.getMaxNumMeteringAreas() > 0) {
							parameters.setMeteringAreas(focusAreas);
						}
					}
					parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
					camera.setParameters(parameters);
					camera.autoFocus(cb);
					System.out.println("#manualFocus");
					camera.unlock();
					return true;
				}
			} catch (Exception e) {
				e.printStackTrace();
				if (e != null)
					Log.e(" ", "autoFocus", e);
			}
		}
		return false;
	}

	@Override
	public void onError(MediaRecorder mr, int what, int extra) {
		try {
			if (mr != null)
				mr.reset();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}